// 具体实现函数
#include "head.h"
#include "fun.h"

// 输出命令选项
void print_options()
{
    printf("         1.cd          进入对应目录\n");
    printf("         2.ls          列出相应目录文件\n");
    printf("         3.puts file   将本地文件上传至服务器\n");
    printf("         4.gets file   下载服务器文件到本地\n");
    printf("         5.remove file 删除服务器上文件\n");
    printf("         6.pwd         显示目前所在路径\n");
    printf("         7.其他命令错误，重新输入\n");
}
/**
 * @brief 可靠接收指定长度的数据
 * @param fd 套接字文件描述符
 * @param buf 接收数据的缓冲区
 * @param len 期望接收的字节数
 * @return 成功返回0，失败返回-1（连接中断或错误）
 */
int recvn(int fd, const void *buf, int len)
{
    int total = 0;         // 已接收的字节数
    char *p = (char *)buf; // 缓冲区指针
    while (total < len)
    {
        int ret = recv(fd, p + total, len - total, 0);
        if (ret == 0)
        {
            // 对端关闭连接
            fprintf(stderr, "Connection closed by peer\n");
            return -1;
        }
        else if (ret == -1)
        {
            // 处理系统调用被信号中断的情况
            if (errno == EINTR)
                continue;
            perror("recv error");
            return -1;
        }
        total += ret; // 更新已接收字节数
    }
    return 0;
}
/**
 * @brief 可靠发送指定长度的数据
 * @param fd 套接字文件描述符
 * @param buf 待发送数据的缓冲区
 * @param len 期望发送的字节数
 * @return 成功返回0，失败返回-1（发送错误）
 */
int sendn(int fd, const void *buf, int len)
{
    int total = 0;               // 已发送的字节数
    const char *p = (char *)buf; // 缓冲区指针
    while (total < len)
    {
        int ret = send(fd, p + total, len - total, MSG_NOSIGNAL);
        if (ret == -1)
        {
            // 处理系统调用被信号中断的情况
            if (errno == EINTR)
                continue;
            perror("send error");
            return -1;
        }
        total += ret; // 更新已发送字节数
    }
    return 0;
}

int login(int sock_fd)
{
    char username[1000] = {0};
    printf("Please enter username:\n");
    fgets(username, 999, stdin);
    if (username[strlen(username) - 1] == '\n')
    {
        username[strlen(username) - 1] = '\0';
    }
    if (strlen(username) == 0)
    {
        printf("Illegal username.\n");
        return -1;
    }
    // 发送用户名，检验用户名合法性
    train_t train;
    bzero(&train, sizeof(train));
    train.len = strlen(username);
    memcpy(train.buff, username, train.len);
    send(sock_fd, &train, sizeof(train.len) + sizeof(train.type) + train.len, 0);
    // 解析收到的数据
    recv(sock_fd, &train, sizeof(train.len) + sizeof(train.type), MSG_WAITALL);
    if (train.len == 0)
    {
        printf("Illegal username.\n");
        return -1;
    }
    // 接收到salt
    recv(sock_fd, train.buff, train.len, MSG_WAITALL);
    char salt[1000] = {0};
    memcpy(salt, train.buff, train.len);
    char password[1000] = {0};
    printf("Please enter password:\n");
    fgets(password, 999, stdin);
    if (password[strlen(password) - 1] == '\n')
    {
        password[strlen(password) - 1] = '\0';
    }
    if (strlen(password) == 0)
    {
        printf("Illegal password.\n");
        return -1;
    }
    // 加密
    char *encrypted_password = crypt(password, salt);
    bzero(train.buff, train.len);
    train.len = strlen(encrypted_password);
    memcpy(train.buff, encrypted_password, train.len);
    send(sock_fd, &train, sizeof(train.len) + sizeof(train.type) + train.len, 0);
    // 查收校验结果
    recv(sock_fd, &train, sizeof(train.len) + sizeof(train.type), MSG_WAITALL);
    if (train.len == 0)
    {
        printf("Illegal password.\n");
        return -1;
    }
    else if (train.len == 1)
    {
        recv(sock_fd, train.buff, train.len, MSG_WAITALL); // 一定要全部接收服务端传回的数据，防止其他接收操作的半包问题
        printf("Login successfully.\n");
    }
    else
    {
        printf("Wrong in password validation.\n");
        return -1;
    }
    return 0;
}
// 加载配置文件（IP、端口、数据库信息）
int get_config(char *filename, Config *config)
{
        FILE *file = fopen(filename, "r");
        ERROR_CHECK(file, NULL, "fopen config");
        char line[256];
        while (fgets(line, sizeof(line), file))
        {
            if (line[0] == '#' || line[0] == '\n')
                continue; // 跳过注释和空行
            char key[64], value[64];
            sscanf(line, "%[^=]=%s", key, value);
            if (strcmp(key, "ip") == 0)
                strcpy(config->ip, value);
            else if (strcmp(key, "port") == 0)
                config->port = atoi(value);
        }
        fclose(file);
        return 0;
}

// 判断字符命令类型
CmdType getCommandType(const char *str)
{
    if (!strcmp(str, "pwd"))
        return CMD_TYPE_PWD;
    else if (!strcmp(str, "ls"))
        return CMD_TYPE_LS;
    else if (!strcmp(str, "cd"))
        return CMD_TYPE_CD;
    else if (!strcmp(str, "mkdir"))
        return CMD_TYPE_MKDIR;
    else if (!strcmp(str, "rmdir"))
        return CMD_TYPE_RMDIR;
    else if (!strcmp(str, "puts"))
        return CMD_TYPE_PUTS;
    else if (!strcmp(str, "gets"))
        return CMD_TYPE_GETS;
    else
        return CMD_TYPE_NOTCMD;
}
// 读取并分离命令 ”操作 对象“
void split_cmd(char *cmd, char *op, char *ob)
{
    int i = 0;
    char *p = cmd;
    while (*p != '\n' && *p != ' ')
    {
        op[i++] = *(p++);
    }
    i = 0;
    if (*p != '\n')
    {
        p++;
    }
    while (*p != '\n')
    {
        ob[i++] = *(p++);
    }
}
// path 当前路径
int command_run(int sock_fd, char *path) // 命令运行
{
    char cmd[256] = {0};
    char op[16] = {0};
    char ob[256] = {0};
    printf("----------><----------\n");
    printf("%s\n", path);
    printf("----------><----------\n");
    int ret = read(STDIN_FILENO, cmd, sizeof(cmd)); // 读取命令
    ERROR_CHECK(ret, -1, "read");
    split_cmd(cmd, op, ob); // 分离命令'操作' '对象'
    printf("op:%s ob:%s\n", op, ob);
    train_t train;
    bzero(&train, sizeof(train));
    int data_len = 0;

    // 发送命令类型
    train.type = getCommandType(op);
    send(sock_fd, &train, sizeof(train.len) + sizeof(train.type), 0);
    if (strcmp(op, "cd") == 0) // 改变路径
    {
        data_len = 0;
        bzero(path, sizeof(path));
        ret = recvn(sock_fd, &data_len, 4); // 接收文件长度
        ERROR_CHECK(ret, -1, "recvn");
        ret = recvn(sock_fd, path, data_len); // 接收路径
        ERROR_CHECK(ret, -1, "recvn");
    }
    else if (strcmp(op, "pwd") == 0)
    {
        data_len = 0;
        bzero(path, sizeof(path));
        ret = recvn(sock_fd, &data_len, 4);
        ERROR_CHECK(ret, -1, "recvn");
        ret = recvn(sock_fd, path, data_len);
        ERROR_CHECK(ret, -1, "recvn");
        printf("%s\n", path);
    }
    else if (strcmp(op, "ls") == 0) // 列出文件
    {
        char buff[1024] = {0};
        while (1)
        {
            recvn(sock_fd, &data_len, sizeof(data_len));
            if (data_len == 0)
            {
                break;
            }
            bzero(buff, sizeof(buff));
            recvn(sock_fd, buff, data_len);
            printf("%s\n", buff);
        }
    }
    else if (strcmp(op, "gets") == 0) // 接收文件
    {
        int data_len = 0;
        off_t file_size = 0;
        int fd = open(ob, O_RDWR | O_CREAT, 0666);
        ERROR_CHECK(fd, -1, "open");
        printf("%s %s", op, ob);
        ret = recv(sock_fd, &data_len, 4, 0);
        if (ret == -1)
            return -1;
        ret = recv(sock_fd, &file_size, data_len, 0);
        if (ret == -1)
            return -1;
        printf("file_size=%ld\n", file_size);
        // 接收文件内容
        time_t start, end;
        start = time(NULL);
        ftruncate(fd, file_size);
        void *pstart = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        ERROR_CHECK(pstart, (void *)-1, "mmap");
        ret = recvn(sock_fd, pstart, file_size);
        if (ret == -1)
        {
            printf("recv gg\n");
        }
        else
        {
            end = time(NULL);
            printf("100%% use time =%ld\n", end - start);
        }
        munmap(pstart, file_size);
        close(fd);
    }
    else if (strcmp(op, "puts") == 0) // 上传文件
    {
        printf("start upload\n");
        printf("path = %s\n", getcwd(NULL, 0));
        // ret = tran_file(sock_fd, ob);
        if (ret == -1)
            printf("tran_file gg\n");
        printf("Send %s Success\n", ob);
    }
    else if (strcmp(op, "remove") == 0)
    {
        printf("rm %s Success\n", ob);
    }
    else if (strcmp(op, "exit") == 0)
    {
        return 1; // 返回1表示退出
    }
    else
    {
        printf("ERROR CMD，请重新输入\n");
    }
    return 0; // 继续处理命令
}
